<!--
 * @Description: zzb
 * @Version: 0.0.1
 * @Autor: zzb
 * @Date: 2022-07-02 09:56:02
 * @LastEditors: zzb
 * @LastEditTime: 2023-08-28 16:02:42
-->
<template>
  <!-- 上传器 -->
  <uploader
    ref="uploader"
    :options="options"
    :autoStart="true"
    :file-status-text="fileStatusText"
    @file-added="onFileAdded"
    @file-success="onFileSuccess"
    @file-error="onFileError"
    @file-progress="onFileprogress"
    @upload-start="onUploadStart"
    @complete="onCatchAll"
    class="uploader-ui"
  >
    <!-- <uploader-unsupport></uploader-unsupport> -->
    <uploader-btn id="global-uploader-btn" :attrs="attrs" ref="uploadBtn">
      <slot> 大视频上传<i class="el-icon-upload el-icon--right"></i> </slot>
    </uploader-btn>
    <uploader-drop v-if="showDrop">
      <p>将文件拖拽至此处，或选择文件</p>
    </uploader-drop>

    <uploader-list v-if="showFileList"></uploader-list>
  </uploader>
</template>

<script>
import { mapState } from "vuex";
import SparkMD5 from "spark-md5";
import { getToken } from "@/utils/auth";
import { mergeFile, checkFile } from "@/api/upload";
import { hex_md5 } from "@/utils/md5.js"; // 引入js文件
import { formatFileSize } from "@/utils";
function _10to62(number) {
  let chars = "0123456789abcdefghigklmnopqrstuvwxyzABCDEFGHIGKLMNOPQRSTUVWXYZ";
  let radix = chars.length;
  let arr = [];
  do {
    let mod = number % radix;
    number = (number - mod) / radix;
    arr.unshift(chars[mod]);
  } while (number);
  return arr.join("");
}
let videoFileExt = ["flv", "mp4", "avi", "wmv"];
let imageFileExt = ["jpg", "jpeg", "png", "gif", "bmp", "jfif"];

let fileSizeLimit = 1 * 1024 * 1024 * 1024 * 10;
export default {
  name: "FenpianUploader",
  props: {
    siteAlias: {
      type: String,
    },
    showFileList: {
      type: Boolean,
      default: false,
    },
    autoStart: {
      type: Boolean,
      default: true,
    },
    showDrop: {
      type: Boolean,
      default: false,
    },
    target: {
      type: String,
      default: "/file/upload",
    },
    accept: {
      type: Array,
      default: () => {
        return [".flv", ".mp4", ".avi", ".wmv"];
      },
    },
  },
  data() {
    return {
      currentChunk: 0,
      options: {
        headers: {
          isToken: false,

          // Authorization: "Bearer " + getToken(),
        },
        // 是否单文件上传   根据上传先后插入在编辑器中
        singleFile: false,
        //目标上传 URL，默认POST
        target: process.env.VUE_APP_BASE_API + this.target,
        //分块大小(单位：字节) 20M
        chunkSize: 100 * 1024 * 1024,

        //上传文件时文件内容的参数名，对应chunk里的Multipart对象名，默认对象名为file
        fileParameterName: "file",
        // 其他额外的参数
        query: (file) => {
          console.log("file: ", file);
          let suffix = file.name.slice(file.name.lastIndexOf(".") + 1);
          let fileType = "docs";

          if (videoFileExt.indexOf(suffix) > -1) {
            fileType = "medias";
          } else if (imageFileExt.indexOf(suffix) > -1) {
            fileType = "images";
          }
          file.FileType111 = fileType;
          var key = _10to62(parseInt(this.checkMd5(file), 16));
          return {
            suffix,
            fileType,
            key,
            // fileName:file.name,
            siteAlias: this.siteAlias,
          };
        },
        // 处理请求参数
        processParams: (params) => {
          console.log("params: ", params);
          return {
            suffix: params.suffix,
            shardIndex: params.shardIndex,
            shardSize: params.currentChunkSize,
            size: params.totalSize,
            shardTotal: params.totalChunks,
            key: params.key,
            siteAlias: params.siteAlias,
            shardIndex: params.chunkNumber,
            fileType: params.fileType,
            // 只有dwdt下显示原名称  其余为文件名字符串
            fileName:
              this.city_name == "dwdt" && params.fileType == "medias"
                ? params.filename
                : "",
          };
        },
        //失败后最多自动重试上传次数
        maxChunkRetries: 3,
        //是否开启服务器分片校验，对应GET类型同名的target URL
        testChunks: false,
        /*
        服务器分片校验函数，判断秒传及断点续传,传入的参数是Uploader.Chunk实例以及请求响应信息
        reponse码是successStatuses码时，才会进入该方法
        reponse码如果返回的是permanentErrors 中的状态码，不会进入该方法，直接进入onFileError函数 ，并显示上传失败
        reponse码是其他状态码，不会进入该方法，正常走标准上传
        checkChunkUploadedByResponse函数直接return true的话，不再调用上传接口
        */
        checkChunkUploadedByResponse: function (chunk, response_msg) {
          let objMessage = JSON.parse(response_msg);
          if (objMessage.skipUpload) {
            return true;
          }
          return (
            (objMessage.uploadedChunks || []).indexOf(chunk.offset + 1) >= 0
          );
        },
      },
      attrs: {
        accept: this.accept,
      },
      fileStatusText: {
        success: "上传成功",
        error: "上传失败",
        uploading: "上传中",
        paused: "暂停",
        waiting: "等待上传",
      },
      loading: null,

      allFileSize: 0, //  多文件上传时的  总大小
      endByte: {}, // 多文件上传的 已上传的文件大小
    };
  },

  components: {},

  computed: {
    ...mapState({
      city_name: (state) => state.user.city_name,
    }),
  },

  mounted() {},

  watch: {
    value: {
      handler(val) {},
      deep: true,
      immediate: true,
    },
  },

  methods: {
    // 上传
    onFileAdded(file) {
      console.log("file: ", file);

      console.log("文件大小：" + file.size);
      console.log("限制大小：" + fileSizeLimit);
      this.allFileSize += file.size;
      if (file.size > fileSizeLimit) {
        this.$message({
          showClose: true,
          message: "文件大小不能超过2G",
        });
        file.cancel();
      }
      if (!this.handlerValidateFileType(file.name)) {
        this.$message.error("文件格式不符合要求!");
        file.cancel();
        return false;
      }
      let checkFileFlag = this.handleCheckFile(file);
      console.log("checkFileFlag: ", checkFileFlag);
      if (checkFileFlag) {
      }
      if (this.autoStart) {
        this.computeMD5(file);
        this.loading = this.$loading({
          lock: true,
          text: "Loading",
          spinner: "el-icon-loading",
          background: "rgba(0, 0, 0, 0.7)",
        });
      }
    },
    onUploadStart() {
      console.log("onUploadStart");
      return;
      if (!this.loading) {
        this.loading = this.$loading({
          lock: true,
          text: "Loading",
          spinner: "el-icon-loading",
          background: "rgba(0, 0, 0, 0.7)",
        });
      }
    },
    handlerValidateFileType(name) {
      let arr = this.attrs.accept;
      let type = name.slice(name.lastIndexOf(".")).toLocaleLowerCase();
      if (arr.includes(type)) {
        return true;
      } else {
        return false;
      }
    },
    handleCheckFile(file) {
      var key = _10to62(parseInt(this.checkMd5(file), 16));
      checkFile({ key: key }).then((res) => {
        console.log("res: ", res);
        if (res.code == 10000) {
          return true;
        } else {
        }
      });
    },
    /*
    第一个参数 rootFile 就是成功上传的文件所属的根 Uploader.File 对象，它应该包含或者等于成功上传文件；
    第二个参数 file 就是当前成功的 Uploader.File 对象本身；
    第三个参数就是 message 就是服务端响应内容，永远都是字符串；
    第四个参数 chunk 就是 Uploader.Chunk 实例，它就是该文件的最后一个块实例，如果你想得到请求响应码的话，chunk.xhr.status就是
    */
    onFileSuccess(rootFile, file, response, chunk) {
      console.log(
        "rootFile, file, response, chunk: ",
        rootFile,
        file,
        response,
        chunk
      );
      let resObj = JSON.parse(response);
      let obj = {
        siteAlias: this.siteAlias,
        fileName: resObj.realName,
        filePath: resObj.storeName,
        url: resObj.url,
        fileSize: formatFileSize(file.size),
        fileType: file.FileType111,
      };
      // this.$emit("input", JSON.parse(response));
      this.$emit("input", obj);

      this.msgSuccess(file.name + "上传成功");
      // this.loading&&this.loading.close();
    },
    onFileError(rootFile, file, response, chunk) {
      console.log("上传完成后异常信息：" + response);
      this.error(file.name + `上传失败，请检查该文件`);
      this.loading && this.loading.close();
    },
    onFileprogress(rootFile, file, chunk) {
      console.log("rootFile, file, chunk: ", rootFile, file, chunk);
      this.endByte[file.id] = chunk.endByte;
      let n = 0;
      for (const key in this.endByte) {
        const item = this.endByte[key];
        n += item;
      }
      // this.$emit("progress", ((chunk.endByte / file.size) * 100).toFixed(2));
      let uploadPercentage = ((n / this.allFileSize) * 100).toFixed(2);
      if (this.autoStart) {
        this.loading.setText("已上传：[" + uploadPercentage + "%]");
      }
    },
    onCatchAll() {
      this.allFileSize = 0;
      this.endByte = {};
      this.loading && this.loading.close();
      console.log("事件上传完毕");
    },
    error(msg) {
      this.$notify({
        title: "错误",
        message: msg,
        type: "error",
        duration: 2000,
      });
    },

    submit() {
      this.computeMD5(file);
      this.loading = this.$loading({
        lock: true,
        text: "Loading",
        spinner: "el-icon-loading",
        background: "rgba(0, 0, 0, 0.7)",
      });
    },
    /**
     * 计算md5，实现断点续传及秒传
     * @param file
     */
    computeMD5(file) {
      let _this = this;
      file.pause();
      //单个文件的大小限制2G

      let fileReader = new FileReader();
      let time = new Date().getTime();
      let blobSlice =
        File.prototype.slice ||
        File.prototype.mozSlice ||
        File.prototype.webkitSlice;
      let currentChunk = 0;
      const chunkSize = 20 * 1024 * 1000;
      console.log("chunkSize: ", chunkSize);
      let chunks = Math.ceil(file.size / chunkSize);
      let spark = new SparkMD5.ArrayBuffer();
      //由于计算整个文件的Md5太慢，因此采用只计算第1块文件的md5的方式
      let chunkNumberMD5 = 1;
      loadNext();
      fileReader.onload = (e) => {
        spark.append(e.target.result);
        if (currentChunk < chunkNumberMD5) {
          loadNext();
        } else {
          let md5 = spark.end();
          console.log(md5);
          file.uniqueIdentifier = md5;
          file.resume();
          console.log(
            `MD5计算完毕：${file.name} \nMD5：${md5} \n分片：${chunks} 大小:${
              file.size
            } 用时：${new Date().getTime() - time} ms`
          );
        }
      };

      fileReader.onerror = function () {
        this.error(`文件${file.name}读取出错，请检查该文件`);
        file.cancel();
      };

      function loadNext() {
        let start = currentChunk * chunkSize;
        let end =
          start + chunkSize >= file.size ? file.size : start + chunkSize;

        fileReader.readAsArrayBuffer(blobSlice.call(file.file, start, end));
        currentChunk++;

        _this.currentChunk = currentChunk;
        console.log("计算第" + currentChunk + "块");
      }
    },

    /**
     * md5 check
     */
    checkMd5(file) {
      return hex_md5(
        file.name + file.size + file.fileType + file.file.lastModified
      );
    },
  },
};
</script>

<style scoped lang="scss">
.uploader-ui {
  display: inline-block;
}
.uploader-btn {
  background: #ffffff;
  border: 1px solid #dcdfe6;
  border-color: #dcdfe6;
  font-weight: 400;
  box-sizing: border-box;
  outline: none;
  margin: 0;
  font-size: 12px;
  padding: 5px 8px;
}
::v-deep .uploader-file-icon {
  display: none !important;
}
</style>
